package com.nengchuan.kbms.dfs.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nengchuan.config.exception.NcException;
import com.nengchuan.kbms.common.PageServiceImpl;
import com.nengchuan.kbms.dfs.annotation.UserOperateLogger;
import com.nengchuan.kbms.dfs.config.PdfConfig;
import com.nengchuan.kbms.dfs.constant.BaseConstant;
import com.nengchuan.kbms.dfs.dao.DataFileMapper;
import com.nengchuan.kbms.dfs.dao.DataFileTagAttrMapper;
import com.nengchuan.kbms.dfs.dao.DataTagAttrMapper;
import com.nengchuan.kbms.dfs.entity.dto.*;
import com.nengchuan.kbms.dfs.entity.po.*;
import com.nengchuan.kbms.dfs.entity.vo.*;
import com.nengchuan.kbms.dfs.util.pdf.ItextPdfUtil;
import com.nengchuan.util.IdGenUtils;
import com.nengchuan.util.MapperUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 文件表 服务实现类
 * </p>
 *
 * @author abe
 * @since 2020-03-24
 */
@Service
public class DataFileServiceImpl extends PageServiceImpl<DataFileMapper, DataFile> implements DataFileService {

    @Autowired
    private DataFileMapper dataFileMapper;

    @Autowired
    private DataTagAttrMapper dataTagAttrMapper;

    @Autowired
    private FdfsService fdfsService;

    @Autowired
    private DataFileInfoService dataFileInfoService;

    @Autowired
    private CommonDicService commonDicService;

    @Autowired
    private PdfConfig pdfConfig;

    @Autowired
    private DataFileTagAttrMapper dataFileTagAttrMapper;

    @Autowired
    private DataFileTagAttrService dataFileTagAttrService;

    @Autowired
    private DataFolderService dataFolderService;


    @Override
    public DataFileVO uploadFile(DataFileUploadDTO uploadDTO, InputStream inputStream) {
        // TODO 检查 folderId 是否存在
        uploadDTO.setId(null);
        try {
            uploadDTO.setFileSize((long) inputStream.available());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // TODO 考虑文件更新的问题
        // 上传到 fdfs
        String fullPath = fdfsService.uploadFile(inputStream, uploadDTO.getFileSize(), getSuffix(uploadDTO.getFileName()));
        uploadDTO.setRealFilePath(fullPath);

        // 数据入库
        DataFile dataFile = insert(uploadDTO);
        if (dataFile == null) {
            throw new NcException("文件入库失败: " + uploadDTO);
        }
        DataFileVO dataFileVO = getById(dataFile.getId(), DataFileVO.class);

        dataFileVO.setBizType(BaseConstant.BizType.f.name());
        setFileSize4Human(dataFileVO);
        return dataFileVO;
    }

    @Override
    public void downloadFile(DownloadFileDTO downloadFileDTO, OutputStream outputStream) {
        Long fileId = downloadFileDTO.getId();
        DataFile dataFile = getById(fileId);
        if (dataFile == null) {
            throw new NcException("错误的文件id: " + fileId);
        }
        //fdfsService.downloadFile(outputStream, dataFile.getRealFilePath());
        String filePath = pdfConfig.getPdfPath() + fileId + ".pdf";
        File file = new File(filePath);
        //如果文件存在就不去dfs服务器中获取文件数据,直接返回
        if (file.exists()) {
            try {
                byte[] byteFromFile = ItextPdfUtil.getByteFromFile(filePath);
                outputStream.write(byteFromFile);
                outputStream.close();
            } catch (Exception e) {
                throw new NcException("获取文件错误: " + fileId);
            }
        } else {
            fdfsService.downloadFile(outputStream, dataFile, downloadFileDTO);
        }

    }

    /**
     * 文件（夹）列表
     */
    @Override
    public IPage<DataFileVO> folderAndFilePage(DataFilePageDTO pageDTO) {
        IPage<DataFileVO> page = dataFileMapper.folderAndFilePage(pageDTO);
        page.getRecords().forEach(vo -> {
            setFileSize4Human(vo);
            if (BaseConstant.BizType.f.name().equals(vo.getBizType())) {
                // 设置文件类型
                String suffix = getSuffix(vo.getFileName()).toUpperCase();
                vo.setType(commonDicService.getName(BaseConstant.TypeCode.EXT_TYPE, suffix));
            }
        });
        return page;
    }

    /**
     * 断言文件存在
     */
    @Override
    public DataFile assertFileExisted(Long fileId) {
        DataFile dataFile = getById(fileId);
        if (dataFile == null) {
            throw new NcException("文件不存在: " + fileId);
        }
        return dataFile;
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName
     * @return
     */
    @Override
    public String getSuffix(String fileName) {
        if (!StringUtils.isEmpty(fileName)) {
            int index = fileName.lastIndexOf(".");
            if (index >= 0) {
                return fileName.substring(index + 1);
            }
        }
        return "";
    }

    /**
     * 设置友好可读的文件大小信息
     *
     * @param vo
     */
    private void setFileSize4Human(DataFileVO vo) {
        if (vo != null) {
            if (vo.getFileSize() != null) {
                vo.setFileSizeStr(bytes4Human(vo.getFileSize()));
            }
        }
    }


    /**
     * 通过字节数返回人类可读的字符
     * 不支持 TB 级
     *
     * @param bytes
     * @return
     */
    @Override
    public String bytes4Human(long bytes) {
        String value = "";
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes >= 1024 * 1024 * 1024) {
            value = String.format("%.2f GB", bytes / 1f / (1024 * 1024 * 1024));
        } else if (bytes >= 1024 * 1024) {
            value = String.format("%.2f MB", bytes / 1f / (1024 * 1024));
        } else if (bytes >= 1024) {
            value = String.format("%.2f KB", bytes / 1024f);
        }
        return value.replace(".00", "");
    }

    /**
     * 获取文件详情
     */
    @Override
    public FileDetailVO getDetail(Long id) {
        FileDetailVO fileDetailVO = new FileDetailVO();
        // 基本信息
        FileBasicVO basic = getById(id, FileBasicVO.class);
        if (basic == null) {
            log.warn("文件不存在，id：" + id);
            return null;
        }
        basic.setFileSizeStr(bytes4Human(basic.getFileSize()));
        String type = commonDicService.getName(BaseConstant.TypeCode.EXT_TYPE, getSuffix(basic.getFileName()).toUpperCase());
        basic.setType(type);
        fileDetailVO.setBasic(basic);
        DataFileInfo info = dataFileInfoService.getById(id);
        if (info != null) {
            basic.setIsbn(info.getIsbn());
            basic.setPreviewCount(info.getPreviewCount());
            basic.setDownloadCount(info.getDownloadCount());
        }
        // TODO 获取其他信息（元数据信息、历史文件、关联文件）
        //获取文件的标签属性信息
        List<DataFileTagAttr> tagAttrList = dataFileTagAttrMapper.getFileTagAttr(id);
        List<DataFileTagAttrVO> tagAttrVOList = MapperUtils.mappingList(tagAttrList, DataFileTagAttrVO.class);
        fileDetailVO.setFileTagAttr(tagAttrVOList);
        return fileDetailVO;
    }

    /**
     * 解析文件名,上传文件,保存文件夹和标签属性
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public DataFileVO resolveFileName(MultipartFile file, String folderName) throws IOException {
        String[] fileNameResolvedArray = file.getOriginalFilename().split("-");
        String firstFolderName = null;
        String secondFolderName = null;
        String thirdFolderName = null;
        String fourthFolderName = null;
        for (int i = 0; i < fileNameResolvedArray.length; i++) {
            firstFolderName = fileNameResolvedArray[0];//设备类型
            secondFolderName = fileNameResolvedArray[1];//电压等级
            thirdFolderName = fileNameResolvedArray[2];//厂家
            fourthFolderName = fileNameResolvedArray[3] + "-" + fileNameResolvedArray[4] + "-" + fileNameResolvedArray[5];//型号
        }
        //保存解析后的四个文件夹
        long fourthFolderId = saveResolvedFolders(firstFolderName, secondFolderName, thirdFolderName, fourthFolderName);
        //文件上传至fastDFS
        DataFileUploadDTO dto = new DataFileUploadDTO();
        dto.setFileName(file.getOriginalFilename());
        dto.setFileSize(file.getSize());
        dto.setFolderId(fourthFolderId);
        DataFileVO dataFileVO = uploadFile(dto, file.getInputStream());
        //保存解析后的标签属性信息
        saveFileNameReslovedTagAttr(folderName, dataFileVO, firstFolderName, secondFolderName, thirdFolderName, fourthFolderName);
        return dataFileVO;
    }


    /**
     * 保存解析文件名后的四个文件夹
     */
    private Long saveResolvedFolders(String firstFolderName, String secondFolderName, String thirdFolderName, String fourthFolderName) {
        //保存第一级文件夹,先查询是否存在,不存在则创建
        DataFolder dataFolderPo = dataFolderService.queryFolder(101L, firstFolderName);
        Long parentFolderId = 101L;
        if (Objects.isNull(dataFolderPo)) {
            parentFolderId = saveDataFolder(parentFolderId, firstFolderName);
        } else {
            parentFolderId = dataFolderPo.getId();
            dataFolderPo = dataFolderService.queryFolder(parentFolderId, secondFolderName);
        }

        //保存第二级文件夹,先查询是否存在,不存在则创建
        if (Objects.isNull(dataFolderPo)) {
            parentFolderId = saveDataFolder(parentFolderId, secondFolderName);
        } else {
            parentFolderId = dataFolderPo.getId();
            dataFolderPo = dataFolderService.queryFolder(parentFolderId, thirdFolderName);
        }

        //保存第三级文件夹,先查询是否存在,不存在则创建
        if (Objects.isNull(dataFolderPo)) {
            parentFolderId = saveDataFolder(parentFolderId, thirdFolderName);
        } else {
            parentFolderId = dataFolderPo.getId();
            dataFolderPo = dataFolderService.queryFolder(parentFolderId, fourthFolderName);
        }

        //保存第四级文件夹,先查询是否存在,不存在则创建
        if (Objects.isNull(dataFolderPo)) {
            return saveDataFolder(parentFolderId, fourthFolderName);
        } else {
            return dataFolderPo.getId();
        }
    }

    /**
     * 保存文件夹的通用方法
     */
    private Long saveDataFolder(Long parentFolderId, String folderName) {
        DataFolderDTO folderDTO = new DataFolderDTO();
        folderDTO.setFolderName(folderName);
        folderDTO.setParentId(parentFolderId);
        return dataFolderService.saveFolder(folderDTO);
    }


    /**
     * 保存解析文件名后的标签属性
     */
    private void saveFileNameReslovedTagAttr(String folderName, DataFileVO dataFileVO,
                                             String firstFolderName, String secondFolderName,
                                             String thirdFolderName, String fourthFolderName) {
        Long fileId = dataFileVO.getId();
        saveFileTagAttr("厂家名称", thirdFolderName, fileId);
        saveFileTagAttr("设备类型", firstFolderName, fileId);
        saveFileTagAttr("电压等级", secondFolderName, fileId);
        saveFileTagAttr("设备型号", fourthFolderName, fileId);
        saveFileTagAttr("资料版本", "V.01", fileId);
        String[] folderNameArray = folderName.split("-");
        String dataTypeValue = folderNameArray[folderNameArray.length - 1];
        saveFileTagAttr("资料类型", dataTypeValue, fileId);
    }

    /**
     * 保存文件标签属性的通用方法
     */
    private void saveFileTagAttr(String attrName, String attrValue, Long fileId) {
        DataFileTagAttrDTO fileTagAttrDTO = new DataFileTagAttrDTO();
        fileTagAttrDTO.setFileId(fileId);
        fileTagAttrDTO.setId(IdGenUtils.genId());
        fileTagAttrDTO.setAttrName(attrName);
        fileTagAttrDTO.setAttrValue(attrValue);
        dataFileTagAttrService.saveFileTagAttr(fileTagAttrDTO);
    }

}
